using System;
using System.Net;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;

using WebServer.HTDOCS;
using WebServer.Modules;

namespace WebServer
{
	public enum FileBufferState
	{
		Buffering,
		Stopped,
		Crashed
	}

	public class FileBuffer : IDisposable
	{
		private static long _DefaultBufferLength = 5242880; //5MB
		public static long DefaultBufferLength { get { return _DefaultBufferLength; } set { _DefaultBufferLength = value; } }

		private FileBufferState _State = FileBufferState.Stopped;
		public FileBufferState State { get { return _State; } }

		private FileInfo _File;
		public FileInfo File { get { return _File; } }

		private Stream _Stream;
		public Stream Stream { get { return _Stream; } }

		private long _MaxBufferLength = DefaultBufferLength;
		public long MaxBufferLength { get { return _MaxBufferLength; } }

		private byte[] _Buffer;
		public byte[] Buffer { get { return _Buffer; } }

		private long _Processed;
		public long Processed { get { return _Processed; } }

		private long _ChunkSize;
		public long ChunkSize { get { return _ChunkSize; } }

		private long _Remaining;
		public long Remaining { get { return _Remaining; } }

		private string _StatusMessage;
		public string StatusMessage { get { return _StatusMessage; } }

		public FileBuffer(string path)
			: this(path, DefaultBufferLength)
		{ }

		public FileBuffer(string path, long maxBufferLength)
			: this(new FileInfo(path), maxBufferLength)
		{ }

		public FileBuffer(FileInfo file)
			: this(file, DefaultBufferLength)
		{ }

		public FileBuffer(FileInfo file, long maxBufferLength)
		{
			_File = file;
			_MaxBufferLength = maxBufferLength;
		}

		public void Dispose()
		{
			Stop("Disposing.");
		}

		public void Stop(string reason)
		{
			_Buffer = new byte[0];
			_State = FileBufferState.Stopped;
			_StatusMessage = reason;
		}

		public void Crash(string reason)
		{
			_Buffer = new byte[0];
			_State = FileBufferState.Crashed;
			_StatusMessage = reason;
		}

		public void Start(Stream stream)
		{
			Start(stream, DefaultBufferLength);
		}

		public void Start(Stream stream, long maxBufferLength)
		{
			_Stream = stream;

			if (_State == FileBufferState.Buffering)
			{
				Stop("Buffering in progress.");
				return;
			}

			if (_File == null)
			{
				Stop("File is null.");
				return;
			}

			_State = FileBufferState.Buffering;

			_Processed = 0;
			_ChunkSize = _MaxBufferLength;
			_Remaining = _File.Length;

			using (StreamReader reader = new StreamReader(_File.FullName))
			{
				while (_Remaining > 0 && _Stream != null && _State == FileBufferState.Buffering)
				{
					try
					{
						if (_ChunkSize > _Remaining)
							_ChunkSize = _Remaining;

						if (_Remaining <= 0)
						{
							Stop("No data left.");
							return;
						}

						_Buffer = new byte[_ChunkSize];

						Explode(reader.BaseStream);

						_Stream.Write(_Buffer, 0, _Buffer.Length);

						_Processed += _ChunkSize;
						_Remaining -= _ChunkSize;

						Core.ConsoleWriteLine(
							"F: {0} | C: {1} | P: {2} | L: {3}",
							_File.Name, _ChunkSize, _Processed, _Remaining);

						_Buffer = new byte[0];
					}
					catch (Exception ex) { Crash(ex.ToString()); }
				}

				reader.Close();
			}
		}

		private void Explode(Stream reader)
		{
			int smallDataProcessed = 0;
			int smallChunkSize = (int)(_ChunkSize / 1024);
			int smallChunkCount = (int)(_ChunkSize / smallChunkSize);
			int smallDataLeft = (int)(smallChunkSize * smallChunkCount);

			for (int i = 0; i < smallChunkCount; i++)
			{
				if (smallChunkSize > smallDataLeft)
					smallChunkSize = smallDataLeft;

				reader.Read(_Buffer, smallDataProcessed, smallChunkSize);

				smallDataProcessed += smallChunkSize;
				smallDataLeft -= smallChunkSize;
			}
		}
	}
}